package com.clp.protocol.iec104.client.config;

import com.clp.protocol.core.common.Checkable;
import com.clp.protocol.core.utils.LocalHostDoesNotExistException;
import com.clp.protocol.core.utils.AssertUtil;
import com.clp.protocol.core.utils.IpUtil;
import com.clp.protocol.core.utils.StringUtil;
import lombok.Getter;

/**
 * 主站连接配置
 */
@Getter
public class MasterConfig implements Checkable {
    /**
     * 默认本地ip
     */
    public static final String DEFAULT_LOCAL_HOST = IpUtil.getLocalIpSet().iterator().next();
    /**
     * 默认本地端口号（0表示随机本地端口）
     */
    public static final int DEFAULT_LOCAL_PORT = 0;
    /**
     * 默认远程端口号
     */
    public static final int DEFAULT_REMOTE_PORT = 2404;
    /**
     * 默认公共地址
     */
    public static final int DEFAULT_LOCAL_RTU_ADDR = 1;
    public static final int DEFAULT_REMOTE_RTU_ADDR = 1;

    /******************************************************************************************************************/

    private final String localHost;
    private final int localPort;
    private final int localRtuAddr;
    private final String remoteHost;
    private final int remotePort;
    private final int remoteRtuAddr;

    private final MasterControlConfig controlConfig;
    private final MasterDataConfig dataConfig;

    private MasterConfig(Configurer configurer) {
        this.localHost = configurer.localHost;
        this.localPort = configurer.localPort;
        this.localRtuAddr = configurer.localRtuAddr;
        this.remoteHost = configurer.remoteHost;
        this.remotePort = configurer.remotePort;
        this.remoteRtuAddr = configurer.remoteRtuAddr;

        this.controlConfig = configurer.controlConfig;
        this.dataConfig = configurer.dataConfig;
    }

    @Override
    public void check() throws Throwable {
        // 检查 ip 是否合法，本地ip是否存在
        if (StringUtil.isBlank(localHost) || StringUtil.isBlank(remoteHost)) {
            throw new RuntimeException("非法的本地IP地址或远程IP地址！");
        }
        if (!IpUtil.hasLocalHost(localHost)) {
            throw new LocalHostDoesNotExistException(localHost);
        }
        AssertUtil.notNull(controlConfig, "controlConfig");
        controlConfig.check();
        AssertUtil.notNull(dataConfig, "dataConfig");
        dataConfig.check();
    }

    public static Configurer configurer() {
        return new Configurer();
    }

    public static class Configurer {
        private String localHost = DEFAULT_LOCAL_HOST;
        private int localPort = DEFAULT_LOCAL_PORT;
        protected int localRtuAddr = DEFAULT_LOCAL_RTU_ADDR;

        private String remoteHost;
        private int remotePort = DEFAULT_REMOTE_PORT;
        private int remoteRtuAddr = DEFAULT_REMOTE_RTU_ADDR;

        private MasterControlConfig controlConfig;
        private MasterDataConfig dataConfig;

        public Configurer local(int localRtuAddr) {
            return local(DEFAULT_LOCAL_HOST, DEFAULT_LOCAL_PORT, localRtuAddr);
        }

        public Configurer local(String localHost, int localPort) {
            return local(localHost, localPort, DEFAULT_LOCAL_RTU_ADDR);
        }

        public Configurer local(String localHost, int localPort, int localRtuAddr) {
            this.localHost = localHost;
            this.localPort = localPort;
            this.localRtuAddr = localRtuAddr;
            return this;
        }

        public Configurer remote(String remoteHost, int remotePort) {
            return remote(remoteHost, remotePort, DEFAULT_REMOTE_RTU_ADDR);
        }

        public Configurer remote(String remoteHost, int remotePort, int remoteRtuAddr) {
            this.remoteHost = remoteHost;
            this.remotePort = remotePort;
            this.remoteRtuAddr = remoteRtuAddr;
            return this;
        }

        public Configurer controlConfig(MasterControlConfig controlConfig) {
            this.controlConfig = controlConfig;
            return this;
        }

        public Configurer dataConfig(MasterDataConfig dataConfig) {
            this.dataConfig = dataConfig;
            return this;
        }

        public MasterConfig configOk() {
            return new MasterConfig(this);
        }
    }
}
